package edu.hawaii.halealohacli.processor;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
import org.wattdepot.client.WattDepotClient;
import edu.hawaii.halealohacli.command.Command;
import edu.hawaii.halealohacli.command.CurrentPower;
import edu.hawaii.halealohacli.command.DailyEnergy;
import edu.hawaii.halealohacli.command.EnergySince;
import edu.hawaii.halealohacli.command.MonitorGoal;
import edu.hawaii.halealohacli.command.MonitorPower;
import edu.hawaii.halealohacli.command.RankTowers;
import edu.hawaii.halealohacli.command.SetBaseline;

/**
 * The command-line interaction processor.
 * When a new command is made, a reference to it should
 * made in Processor.
 * 
 * @author Chris Fang
 */
public class Processor {

  private boolean testing;
  private WattDepotClient client;
  private Map<String, Command> commandMap;
  private Scanner inputReader;
  private String helpOutput;
  static final int QUIT = 0;
  static final int COMMAND = 1;
  static final int HELP = 2;
  static final int ERROR = 3;

  /**
   * Constructs the Processor.
   * When making a new command, be sure to add it to the commandMap.
   * If you don't, the commands won't be processed correctly.
   * @param client A client that is connected to a WattDepot server
   */
  public Processor(WattDepotClient client) {
    this.client = client;
    inputReader = new Scanner(System.in);
    commandMap = new HashMap<String, Command>();

    // Populate commands map with available commands.
    CurrentPower currentPower = new CurrentPower();
    commandMap.put(currentPower.getName(), currentPower);
    DailyEnergy dailyEnergy = new DailyEnergy();
    commandMap.put(dailyEnergy.getName(), dailyEnergy);
    RankTowers rankTowers = new RankTowers();
    commandMap.put(rankTowers.getName(), rankTowers);
    EnergySince energySince = new EnergySince();
    commandMap.put(energySince.getName(), energySince);
    MonitorPower monitorPower = new MonitorPower();
    commandMap.put(monitorPower.getName(), monitorPower);
    MonitorGoal monitorGoal = new MonitorGoal();
    commandMap.put(monitorGoal.getName(), monitorGoal);
    SetBaseline setBaseline = new SetBaseline();
    commandMap.put(setBaseline.getName(), setBaseline);
    
    // By default, the testing flag is set to false
    testing = false;
  }

  /**
   * Builds the help message string so it isn't remade each time it prints.
   * 
   * @return The help message to print
   */
  protected String buildHelpMessage() {
    String output;
    output = String.format("Available commands for this system:%n%n");
    for (Command command : commandMap.values()) {
      output = output.concat(String.format("%s:%n", command.getUsage()));
      output = output.concat(String.format("  %s%n%n", command.getDescription()));
    }
    output = output.concat(String.format("%s:%n", "quit"));
    output = output.concat(String.format("  %s%n%n", "Terminates execution"));
    output = output.concat(String.format("Note: Hale Aloha towers are "));
    output = output.concat(String.format("Mokihana, Ilima, Lehua and Lokelani%n"));
    output = output.concat(String.format("Lounges are the tower names followed by a \"-\" "));
    output = output.concat(String.format("followed by one of A, B, C, D, E. "));
    output = output.concat(String.format("For example, Mokihana-A."));
    return output;
  }

  /**
   * Starts the command-line user interaction.
   */
  public void startProcessor() {
    String input;
    boolean quit = false;
    helpOutput = buildHelpMessage();
    while (!quit) {
      System.out.format("%n%s", "> ");
      input = inputReader.nextLine();
      if (processInput(input) == Processor.QUIT) {
        quit = true;
      }
    }
  }

  /**
   * Processes the user input and executes any recognized input.
   * 
   * @param input The user input
   * @return The return code associated with the action taken
   */
  protected int processInput(String input) {
    String[] splitInput = input.split(" ");
    String command = splitInput[0].toLowerCase(Locale.US);
    if (commandMap.containsKey(command)) {
      if (commandMap.get(command).initializeCommand(client, splitInput)) {
        commandMap.get(command).printResults();
      }
      return Processor.COMMAND;
    }
    else if ("help".equals(input.toLowerCase(Locale.US))) {
      System.out.format("%s", getHelpMessage());
      return Processor.HELP;
    }
    else if ("quit".equals(input.toLowerCase(Locale.US))) {
      return Processor.QUIT;
    }
    else {
      System.out.format("The command inputed was not recognized "
          + "(Enter \"help\" to see a list of available commands)");
      return Processor.ERROR;
    }
  }

  /**
   * Returns the message to be printed out when help is the input.
   * 
   * @return The help message to print
   */
  protected String getHelpMessage() {
    return this.helpOutput;
  }

  /**
   * Sets the testing flag for JUnit testing.
   * 
   * @param testing The value to set the testing flag to
   * @return The value of the testing flag
   */
  protected boolean setTesting(boolean testing) {
    this.testing = testing;
    this.helpOutput = buildHelpMessage();
    return this.testing;
  }
}